﻿using System;
using System.Collections.Concurrent;
using System.Timers;
using Missbot.Common;

namespace Missbot.Core.Scheduler
{
    /// <summary>
    /// 定时器服务
    /// </summary>
    public class SchedulerSvc : ISingleton
    {
        /// <summary>
        /// 所有定时器同步字典
        /// </summary>
        private ConcurrentDictionary<string, SchedulerTimer> Timers { get; } = new();

        private static ILogSvc ILogSvc => AutofacSvc.Resolve<ILogSvc>();

        /// <summary>
        /// 按ID移除定时器
        /// </summary>
        /// <param name="Id"></param>
        public void Remove(string Id)
        {
            if (!Timers.TryGetValue(Id, out var timer) || timer == null)
            {
                return;
            }

            timer.Stop();
            Timers.TryRemove(Id, out _);
        }

        /// <summary>
        /// 添加定时器
        /// </summary>
        /// <param name="Span"></param>
        /// <param name="CallBack"></param>
        /// <param name="Data"></param>
        /// <param name="IsRepeat"></param>
        /// <param name="IsImmdiately"></param>
        public void Add(IScheduleSpan Span, Action<SchedulerTimer> CallBack, object Data = null, bool IsRepeat = true, bool IsImmdiately = false)
        {
            var job = new SchedulerTimer
            {
                AutoReset = false,
                Span = Span,
                Interval = Span.GetNextTriggerSpan().TotalMilliseconds,
                Enabled = true,
                CallBack = CallBack,
                Data = Data,
                IsRepeat = IsRepeat
            };
            job.Elapsed += TimeUp;
            if (IsImmdiately)
            {
                CallBack(job);
            }

            var _ = Timers.TryAdd(job.Id, job);
            job.Start();
        }

        /// <summary>
        /// 触发定时器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimeUp(object sender, ElapsedEventArgs e)
        {
            if (!(sender is SchedulerTimer job))
            {
                return;
            }

            job.Stop();

            try
            {
                job.CallBack(job);
            }
            catch (Exception exception)
            {
                ILogSvc.LogError(exception);
            }
            finally
            {
                if (job.IsRepeat)
                {
                    job.Interval = job.Span.GetNextTriggerSpan().TotalMilliseconds;
                    job.Start();
                }
                else
                {
                    Remove(job.Id);
                }
            }
        }
    }
}